#include "pc.hpp"

#if COMPILER_IS_CLANG
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wambiguous-reversed-operator"
    #pragma clang diagnostic ignored "-Wsign-conversion"
    #pragma clang diagnostic ignored "-Wimplicit-int-conversion"
    #pragma clang diagnostic ignored "-Wbitwise-instead-of-logical"
    #pragma clang diagnostic ignored "-Wsign-compare"
    #pragma clang diagnostic ignored "-Wshorten-64-to-32"
    #pragma clang diagnostic ignored "-Wold-style-cast"
    #include "./lab/rs.cpp"
    #pragma clang diagnostic pop
#else
    #pragma GCC diagnostic   push
    #pragma GCC diagnostic   ignored "-Wambiguous-reversed-operator"
    #pragma GCC diagnostic   ignored "-Wsign-conversion"
    #pragma GCC diagnostic   ignored "-Wimplicit-int-conversion"
    #pragma GCC diagnostic   ignored "-Wbitwise-instead-of-logical"
    #pragma clang diagnostic ignored "-Wsign-compare"
    #pragma clang diagnostic ignored "-Wshorten-64-to-32"
    #pragma clang diagnostic ignored "-Wold-style-cast"
    #include "./lab/rs.cpp"
    #pragma GCC diagnostic pop
#endif

using namespace std;

using Packet  = std::vector<unsigned char>;
using Packets = std::vector<Packet>;

int main()
{
	return 0;
	
    auto rs        = RsfecImpl{};
    auto rs_params = rs.get_rs_params(223, 255);

    auto pkts = Packets{};
    pkts.resize(255);

    unsigned char* src_pkts[256]{};
    int            src_sizes[256]{};
    unsigned char* src_size_ptrs[256]{};

    for (auto i = 0uz; i < 223uz; ++i)
    {
        for (auto j = 0uz; j <= i; ++j)
        {
            pkts[i].push_back('A');
        }

        src_pkts[i]      = pkts[i].data();
        src_sizes[i]     = static_cast<int>(pkts[i].size());
        src_size_ptrs[i] = reinterpret_cast<unsigned char*>(&src_sizes[i]);
    }

    for (auto i = 223uz; i < 255uz; ++i)
    {
        pkts[i].resize(512);
        src_pkts[i]      = pkts[i].data();
        src_sizes[i]     = 0;
        src_size_ptrs[i] = reinterpret_cast<unsigned char*>(&src_sizes[i]);

        auto n = rs.fec_encode_packet(
            rs_params, src_pkts, src_sizes, static_cast<int>(i), src_pkts[i]);
        std::cout << i << _I_ << n << std::endl;

        n = rs.fec_encode_length(rs_params,
                                 src_size_ptrs,
                                 sizeof(int),
                                 static_cast<int>(i),
                                 src_size_ptrs[i]);
        std::cout << i << "$" << n << std::endl;
    }

    std::cout << static_cast<char>(pkts[1].front()) << std::endl;
    pkts[1].front() = 'X';
    std::cout << static_cast<char>(pkts[1].front()) << std::endl;

    int received_data_pkt_idxes[258]{};
    for (auto i = 0; i < 256; ++i)
    {
        received_data_pkt_idxes[i] = i + 1;
    }

    int missing_data_pkt_idxes[32]{};
    for (auto i = 0; i < 32; ++i)
    {
        missing_data_pkt_idxes[i] = 223 + i;
    }

    unsigned char* received_pkts[256]{};
    int            received_pkt_sizes[256]{};
    unsigned char* recovered_pkts[256]{};
    auto           recovered_pkt_bufs = Packets{};
    recovered_pkt_bufs.resize(256);

    auto i = 0;
    for (auto& buf : recovered_pkt_bufs)
    {
        buf.resize(1500);
        recovered_pkts[i++] = buf.data();
    }

    for (auto j = 1; j < 223; ++j)
    {
        received_pkts[j - 1] = src_pkts[j];
    }

    for (auto j = 0; j < 32; ++j)
    {
        received_pkts[223 + j - 1] = src_pkts[223 + j];
    }

    missing_data_pkt_idxes[0] = 0;

    auto n = rs.decode_fec_packet(rs_params,
                                  received_pkts,
                                  received_pkt_sizes,
                                  received_data_pkt_idxes,
                                  recovered_pkts,
                                  missing_data_pkt_idxes,
                                  32);
    std::cout << n << std::endl;

    // n = rs.decode_fec_length(rs_params,
    //                          src_size_ptrs,
    //                          sizeof(int),
    //                          received_pkt_idxes,
    //                          src_size_ptrs + 223,
    //                          received_fec_idxes,
    //                          32);
    // std::cout << n << std::endl;
    std::cout << static_cast<char>(pkts[1].front()) << std::endl;

    auto fin =
        std::ifstream("/home/liuyaoxin/code/memo/rtt.txt", std::ios::binary);
    if (!fin)
    {
        std::cerr << "Invalid fin" << std::endl;
        std::quick_exit(EXIT_FAILURE);
    }

    std::vector<double> pts;

    auto line = std::string{};

    while (std::getline(fin, line))
    {
        pts.push_back(std::stod(line));
    }

    ::ranges::sort(pts);

    for (auto const ts : pts)
    {
        std::cout << ts << std::endl;
    }

    auto const cnt = pts.size();

    auto const idx_pct99 = cnt * 99 / 100;
    auto const idx_pct95 = cnt * 95 / 100;
    auto const idx_pct90 = cnt * 90 / 100;
    auto const idx_pct75 = cnt * 75 / 100;
    auto const idx_pct50 = cnt * 50 / 100;

    std::cout << "Total|PctIndexes: " << cnt << _I_ << idx_pct99 << _I_
              << idx_pct95 << _I_ << idx_pct90 << _I_ << idx_pct75 << _I_
              << idx_pct50 << std::endl;

    std::cout << "pt_pct_99: " << pts[idx_pct99] << std::endl;
    std::cout << "pt_pct_95: " << pts[idx_pct95] << std::endl;
    std::cout << "pt_pct_90: " << pts[idx_pct90] << std::endl;
    std::cout << "pt_pct_75: " << pts[idx_pct75] << std::endl;
    std::cout << "pt_pct_50: " << pts[idx_pct50] << std::endl;
}

/*
auto const svr_ep = LocalEndpoint("127.0.0.1"_ip4, Port(8080));

// Echo server handler
Future<> onNewClientConn(TcpSocket tcp_skt, RemoteEndpoint, LocalEndpoint)
{
    auto       buf    = Buffer(16);
    auto const status = co_await tcp_skt.read(buf, 5s);
    if (status > 0)
    {
        co_await tcp_skt.write(MemView(buf.data(), status), 5s);
    }
}

void benchmark()
{
    sleepFor(1s); // Wait for the Echo server to be ready.

    auto success_cnt           = std::atomic_size_t(0);
    auto failure_cnt           = std::atomic_size_t(0);
    auto rtt_less_than_1ms     = std::atomic_size_t(0);
    auto rtt_less_than_2ms     = std::atomic_size_t(0);
    auto rtt_less_than_3ms     = std::atomic_size_t(0);
    auto rtt_less_than_10ms    = std::atomic_size_t(0);
    auto rtt_greater_than_10ms = std::atomic_size_t(0);

    // Send a string to the Echo server and wait for the response.
    auto const fn_tcp_echo_client = [&] -> Future<>
    {
        auto cli_skt = co_await startTcpClient(svr_ep);

        auto stopwatch = Stopwatch{};
        auto status    = co_await cli_skt.write("hello"sv, 5s);

        if (status > 0)
        {
            auto buf = Buffer(16);
            status   = co_await cli_skt.read(buf, 5s);

            auto const elapsed = stopwatch.elapsed();
            if (elapsed < 1ms)
            {
                ++rtt_less_than_1ms;
            }
            else if (elapsed < 2ms)
            {
                ++rtt_less_than_2ms;
            }
            else if (elapsed < 3ms)
            {
                ++rtt_less_than_3ms;
            }
            else if (elapsed < 10ms)
            {
                ++rtt_less_than_10ms;
            }
            else
            {
                ++rtt_greater_than_10ms;
            }

            if (status > 0 && tosv(buf.data(), status) == "hello"sv)
            {
                ++success_cnt;
                co_return;
            }
        }

        ++failure_cnt;
    };

    auto const total_count = 200'000uz;

    // Start millions of clients concurrently.
    LOOP_N (total_count)
    {
        fn_tcp_echo_client();
    }

    while (success_cnt + failure_cnt < total_count)
    {
        sleepFor(10ms);
    }

    xout("Total Count: "sv, total_count);
    xout("Success Count: "sv, success_cnt.load());
    xout("Failure Count: "sv, failure_cnt.load());
    xout("rtt_less_than_1ms: "sv, rtt_less_than_1ms.load());
    xout("rtt_less_than_2ms: "sv, rtt_less_than_2ms.load());
    xout("rtt_less_than_3ms: "sv, rtt_less_than_3ms.load());
    xout("rtt_less_than_10ms: "sv, rtt_less_than_10ms.load());
    xout("rtt_greater_than_10ms: "sv, rtt_greater_than_10ms.load());
}

int main()
{
    try
    {
        IF_CONSTEVAL
        {
            return 0;
        }
        else
        {
            return 1;
        }

        std::expected<int, std::string> s1 = std::unexpected<std::string>(""s);
        s1                                 = 234;
        s1                                 = std::unexpected(""s);
        auto s                             = std::unexpected(12);
        s                                  = std::unexpected(12);
        auto s3 = std::expected<int, std::string>(std::unexpect, ""s);
        auto s4 = std::expected<int, std::string>(std::unexpect);

        auto s5 = s4;

        initPosixEnv();
        createLogThread("/tmp/stx.log"_fp);
        startTcpServer(svr_ep, onNewClientConn);
        std::thread(benchmark).detach();
        startEventLoop("stx"s);
    }
    catch (std::exception const& e)
    {
        LOG_ERROR("Main thread exited: "sv, e.what());
    }
    catch (...)
    {
        LOG_ERROR("Main thread exited abnormally."sv);
    }

    return EXIT_FAILURE;
}
*/